#include <time.h>
#include <winsock2.h>
#include "MySQL/mysql.h"

#include "CTransport.h"

#include "CEventPacket.h"
#include "CGlobal.h"
#include "CVanaTime.h"
#include "CZoneHandler.h"
#include "CConsole.h"
#include "CUtil.h"


CTransport::CTransport() {
	this->m_arriving = false;
}

CTransport::~CTransport() {

}

void CTransport::arriveAnim() {
//	CConsole::outTime("%s %s, ID %i", this->m_name, this->typeToString(), this->m_id);
	unsigned int hour;
	unsigned int minute;
	CVanaTime::getInstance()->getTime(NULL,NULL,NULL,NULL,&hour,&minute,NULL);
//	CConsole::outTime("   arrival animation at %2i:%02i Vana time", hour, minute);
	this->updateTransNPCs(TRANS_ARRIVE);
	this->m_arriving = true;
}

void CTransport::arriveEvent() {
//	CConsole::outTime("%s %s, ID %i", this->m_name, this->typeToString(), this->m_id);
	unsigned int hour;
	unsigned int minute;
	CVanaTime::getInstance()->getTime(NULL,NULL,NULL,NULL,&hour,&minute,NULL);
//	CConsole::outTime("   arrival event at %i:%02i Vana time", hour, minute);

	CChar * currChar;
	CGlobal::waitCharList(); //Wait for the char list to become free.
	for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
		currChar = CGlobal::getCCharAt(k);
		if (currChar != NULL) {
			if(currChar->loc.zone == this->m_transportZone) {
				//Set char onTransport to this transport's id
				currChar->onTransport = this->m_id;
				//Play transport fade event for chars on transport
				CEventPacket * ptrCep = new CEventPacket(this->m_transEventId);
				ptrCep->createPacket(currChar);
			}
		}
	}
	CGlobal::exitCharList();
}

void CTransport::dock() {
//	CConsole::outTime("%s %s, ID %i", this->m_name, this->typeToString(), this->m_id);
	unsigned int hour;
	unsigned int minute;
	CVanaTime::getInstance()->getTime(NULL,NULL,NULL,NULL,&hour,&minute,NULL);
//	CConsole::outTime("   docked at %i:%02i Vana time", hour, minute);
	this->updateTransNPCs(TRANS_DOCK);
}

void CTransport::depart() {
//	CConsole::outTime("%s %s, ID %i", this->m_name, this->typeToString(), this->m_id);
	unsigned int hour;
	unsigned int minute;
	CVanaTime::getInstance()->getTime(NULL,NULL,NULL,NULL,&hour,&minute,NULL);
//	CConsole::outTime("   departed at %i:%02i Vana time", hour, minute);
	this->updateTransNPCs(TRANS_DEPART);

	//Send the transport departing
	CChar * currChar;
	CGlobal::waitCharList(); //Wait for the char list to become free.
	for(unsigned int k = 0; k < CGlobal::getCCharListSize(); k++) {
		bool isOnTransport = false;
		currChar = CGlobal::getCCharAt(k);
		if (currChar != NULL) {
			if(currChar->loc.zone == this->m_dockZone) {
				isOnTransport = this->isCharOnTransport(currChar);
			}

			// If char is behind dock's invisible door, or in airship area, char 
			if( (currChar->loc.zone == this->m_dockZone) && (isOnTransport) ){
				//Set char onTransport to this transport's id
				currChar->onTransport = this->m_id;
				//Play takeoff event for chars on transport
				CEventPacket * ptrCep = new CEventPacket(this->m_departEventId);  //currChar->loc.zone
				ptrCep->createPacket(currChar);
			}
		}
	}
	CGlobal::exitCharList();
}

void CTransport::despawn() {
//	CConsole::outTime("%s %s, ID %i", this->m_name, this->typeToString(), this->m_id);
	unsigned int hour;
	unsigned int minute;
	CVanaTime::getInstance()->getTime(NULL,NULL,NULL,NULL,&hour,&minute,NULL);
//	CConsole::outTime("   despawned at %i:%02i Vana time", hour, minute);
	this->updateTransNPCs(TRANS_DESPAWN);
	this->m_arriving = false;
}

void CTransport::updateTransNPCs(unsigned short transportStatus) {

	CNpc * transportNpc1;
	if(!(transportNpc1 = CZoneHandler::getInstance()->getNpcByTarg(this->m_targId1, this->m_dockZone))) {
		return;
	}
	CNpc * transportNpc2;
	if(!(transportNpc2 = CZoneHandler::getInstance()->getNpcByTarg(this->m_targId2, this->m_dockZone))) {
		return;
	}
	CNpc * dockNpc;
	if(!(dockNpc = CZoneHandler::getInstance()->getNpcByTarg(this->m_dockTargId, this->m_dockZone))) {
		return;
	}

	time_t rawtime;
	time(&rawtime);
	if(this->m_type = TRANS_AIRSHIP)
		rawtime -= AIRSHIPSTAMP;

	// timestamp to be used for arrival and departure
	transportNpc1->timestamp = (int)rawtime;

	CGlobal::NPCListSemaphore->lock();
	switch(transportStatus) {
		case TRANS_ARRIVE: {
			transportNpc1->status = 0;

			transportNpc1->setAnimation(this->m_arrivalAnim);
			transportNpc1->loc.p = this->m_dockPos;

			transportNpc1->m_name[0] = this->m_NPCname;
			memcpy(transportNpc1->m_name+4, &(transportNpc1->timestamp), 4);
			break;
		}
		case TRANS_DOCK:
			dockNpc->openDoor();
			break;
		case TRANS_DEPART: {
			dockNpc->closeDoor();

			transportNpc1->setAnimation(this->m_departAnim);
			int x = 0xFFFF0000;
			int z = 0xFFFF0000;
			memcpy(&transportNpc1->loc.p.x,&x,sizeof(int));
			memcpy(&transportNpc1->loc.p.z,&z,sizeof(int));

			transportNpc1->m_name[0] = this->m_NPCname;
			memcpy(transportNpc1->m_name+4, &(transportNpc1->timestamp), 4);
			break;
		}
		case TRANS_DESPAWN:
			transportNpc1->status = 2;
			break;
	}

	transportNpc1->sendUpdateZone();
	transportNpc2->sendUpdateZone();
	dockNpc->sendUpdateZone();
	CGlobal::NPCListSemaphore->unlock();
}

void CTransport::resetTransNPCs() {
	CNpc * transportNpc1;
	transportNpc1 = CZoneHandler::getInstance()->getNpcByTarg(this->m_targId1, this->m_dockZone);
	if (transportNpc1 == NULL) {
		CConsole::outErr("Transport not found! Check NPC tables!");
		return;
	}
	CGlobal::NPCListSemaphore->lock();
	transportNpc1->status = 2;  // Despawned
	transportNpc1->m_walkCnt = 32775;
	transportNpc1->alwaysZoneUpdate = true;
	CGlobal::NPCListSemaphore->unlock();

	// Does transport have a second NPC?  Airships have 2 NPCs, unknown for boats/barges.
	if(this->m_targId2 > 0) {
		CNpc * transportNpc2;
		transportNpc2 = CZoneHandler::getInstance()->getNpcByTarg(this->m_targId2, this->m_dockZone);
		if (transportNpc2 == NULL) {
			CConsole::outErr("Transport not found! Check NPC tables!");
			return;
		}
		CGlobal::NPCListSemaphore->lock();
		transportNpc2->status = 2;  // Despawned
		transportNpc2->alwaysZoneUpdate = true;
		CGlobal::NPCListSemaphore->unlock();
	}

	CNpc * dockNpc;
	dockNpc = CZoneHandler::getInstance()->getNpcByTarg(this->m_dockTargId, this->m_dockZone);
	if(dockNpc == NULL) {
		CConsole::outErr("Dock not found! Check NPC tables!");
		return;
	}
	CGlobal::NPCListSemaphore->lock();
	dockNpc->alwaysZoneUpdate = true;
	CGlobal::NPCListSemaphore->unlock();
	dockNpc->closeDoor();
}

char * CTransport::typeToString() {
	switch(this->m_type) {
		case TRANS_AIRSHIP:
			return "airship";
		case TRANS_BOAT:
			return "boat";
		case TRANS_BARGE:
			return "barge";
		default:
			return "notfound";
	};
}

bool CTransport::isArriving() {
	return this->m_arriving;
}

bool CTransport::isCharOnTransport(CChar * schar) {
	bool isOnTransport = false;

	CNpc * dockNpc;
	if(!(dockNpc = CZoneHandler::getInstance()->getNpcByTarg(this->m_dockTargId, this->m_dockZone))) {
		return false;
	}

	// Calculate character's angle from character to dock's invisible door.
	float angle = CUtil::calcAngle(schar->loc.p.x, schar->loc.p.z, dockNpc->loc.p.x, dockNpc->loc.p.z);
	// Calculate distance from char to dock's invisible door.  distance must be <= 5 to be in range
	float distance = CUtil::distance(dockNpc->loc.p, schar->loc.p);
	// Calculate the angle character must be from the dock's invisible door.
	// +90 degrees from tranport's rotation.
	float dockAngle = ((((float)(this->m_dockPos.rotation) / 256) * 360) + 90);
	if(dockAngle >= 360)
		dockAngle -= 360;
	// Character needs to be within 180 degrees on the correct side of dock's invisible door
	float dockCheck1 = dockAngle - 90;
	float dockCheck2 = dockAngle + 90;

	if(dockCheck1 < 0) {
		dockCheck1 += 360;
		if( ((angle > dockCheck1) || (angle < dockCheck2)) && (distance <= 5) )
			isOnTransport = true;
	}
	else if(dockCheck2 > 360) {
		dockCheck2 -= 360;
		if( ((angle > dockCheck1) || (angle < dockCheck2)) && (distance <= 5) )
			isOnTransport = true;
	}
	else if((angle > dockCheck1) && (angle < dockCheck2) && (distance <= 5))
		isOnTransport = true;

	if(schar->boundaryId == this->m_dockBoundaryId)
		isOnTransport = true;

	CConsole::outDebOnly("Angle:%.3f, Distance:%.3f, Dock Angle Check: %.3f, On Transport: %i", angle, distance, dockAngle, isOnTransport);

	return isOnTransport;
}